import type { ReactNode } from 'react';
import { LabelGroup } from '@patternfly/react-core';
import { Table, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table';
import { gql } from '@apollo/client';

import useFeatureFlags from 'hooks/useFeatureFlags';
import useTableSort from 'hooks/useTableSort';
import VulnerabilitySeverityIconText from 'Components/PatternFly/IconText/VulnerabilitySeverityIconText';
import type { VulnerabilityState } from 'types/cve.proto';
import CvssFormatted from 'Components/CvssFormatted';

import AdvisoryLinkOrText from '../../components/AdvisoryLinkOrText';
import PendingExceptionLabel from '../../components/PendingExceptionLabel';
import ImageNameLink from '../components/ImageNameLink';
import {
    flattenDeploymentComponentVulns,
    imageMetadataContextFragment,
    sortTableData,
} from './table.utils';
import type { DeploymentComponentVulnerability, ImageMetadataContext } from './table.utils';
import DockerfileLayer from '../components/DockerfileLayer';
import ComponentLocation from '../components/ComponentLocation';
import FixedByVersion from '../../components/FixedByVersion';

export { imageMetadataContextFragment };
export type { ImageMetadataContext, DeploymentComponentVulnerability };

export const deploymentComponentVulnerabilitiesFragment = gql`
    fragment DeploymentComponentVulnerabilities on ImageComponent {
        name
        version
        location
        source
        layerIndex
        imageVulnerabilities(query: $query) {
            severity
            cvss
            scoreVersion
            fixedByVersion
            advisory {
                name
                link
            }
            discoveredAtImage
            publishedOn
            pendingExceptionCount: exceptionCount(requestStatus: $statusesForExceptionCount)
        }
    }
`;

const sortFields = ['Image', 'Component'];
const defaultSortOption = { field: 'Image', direction: 'asc' } as const;

export type DeploymentComponentVulnerabilitiesTableProps = {
    /** The images and associated component vulnerability data to display in the table */
    images: {
        imageMetadataContext: ImageMetadataContext;
        componentVulnerabilities: DeploymentComponentVulnerability[];
    }[];
    cve: string;
    vulnerabilityState: VulnerabilityState;
};

function DeploymentComponentVulnerabilitiesTable({
    images,
    cve,
    vulnerabilityState,
}: DeploymentComponentVulnerabilitiesTableProps) {
    const { isFeatureFlagEnabled } = useFeatureFlags();
    const isAdvisoryColumnEnabled = isFeatureFlagEnabled('ROX_SCANNER_V4');

    const colSpanForDockerfileLayer = 8 + (isAdvisoryColumnEnabled ? 1 : 0);

    const { sortOption, getSortParams } = useTableSort({ sortFields, defaultSortOption });
    const componentVulns = images.flatMap(({ imageMetadataContext, componentVulnerabilities }) =>
        flattenDeploymentComponentVulns(imageMetadataContext, componentVulnerabilities)
    );
    const sortedComponentVulns = sortTableData(componentVulns, sortOption);

    return (
        <Table
            style={{
                border: '1px solid var(--pf-v5-c-table--BorderColor)',
            }}
            borders={false}
        >
            <Thead noWrap>
                <Tr>
                    <Th sort={getSortParams('Image')}>Image</Th>
                    <Th>CVE severity</Th>
                    <Th>CVSS</Th>
                    <Th sort={getSortParams('Component')}>Component</Th>
                    <Th>Version</Th>
                    <Th>CVE fixed in</Th>
                    {isAdvisoryColumnEnabled && <Th>Advisory</Th>}
                    <Th>Source</Th>
                    <Th>Location</Th>
                </Tr>
            </Thead>
            {sortedComponentVulns.map((componentVuln, index) => {
                const {
                    image,
                    name,
                    severity,
                    version,
                    cvss,
                    scoreVersion,
                    fixedByVersion,
                    advisory,
                    location,
                    source,
                    layer,
                } = componentVuln;
                // No border on the last row
                const style =
                    index !== componentVulns.length - 1
                        ? { borderBottom: '1px solid var(--pf-v5-c-table--BorderColor)' }
                        : {};
                const hasPendingException = componentVulns.some(
                    (vuln) => vuln.pendingExceptionCount > 0
                );
                const labels: ReactNode[] = [];
                if (hasPendingException) {
                    labels.push(
                        <PendingExceptionLabel
                            cve={cve}
                            isCompact
                            vulnerabilityState={vulnerabilityState}
                        />
                    );
                }

                // Td style={{ paddingTop: 0 }} prop emulates vertical space when label was in cell instead of row
                // and assumes adjacent empty cell has no paddingTop.
                return (
                    <Tbody key={`${image.id}:${name}:${version}`} style={style}>
                        <Tr>
                            <Td dataLabel="Image">
                                {image.name ? (
                                    <ImageNameLink name={image.name} id={image.id} />
                                ) : (
                                    'Image name not available'
                                )}
                            </Td>
                            <Td dataLabel="CVE severity" modifier="nowrap">
                                <VulnerabilitySeverityIconText severity={severity} />
                            </Td>
                            <Td dataLabel="CVSS" modifier="nowrap">
                                <CvssFormatted cvss={cvss} scoreVersion={scoreVersion} />
                            </Td>
                            <Td dataLabel="Component">{name}</Td>
                            <Td dataLabel="Version">{version}</Td>
                            <Td dataLabel="CVE fixed in" modifier="nowrap">
                                <FixedByVersion fixedByVersion={fixedByVersion} />
                            </Td>
                            {isAdvisoryColumnEnabled && (
                                <Td dataLabel="Advisory" modifier="nowrap">
                                    <AdvisoryLinkOrText advisory={advisory} />
                                </Td>
                            )}
                            <Td dataLabel="Source">{source}</Td>
                            <Td dataLabel="Location">
                                <ComponentLocation location={location} source={source} />
                            </Td>
                        </Tr>
                        {labels.length !== 0 && (
                            <Tr>
                                <Td colSpan={colSpanForDockerfileLayer} style={{ paddingTop: 0 }}>
                                    <LabelGroup numLabels={labels.length}>{labels}</LabelGroup>
                                </Td>
                            </Tr>
                        )}
                        <Tr>
                            <Td colSpan={colSpanForDockerfileLayer} className="pf-v5-u-pt-0">
                                <DockerfileLayer layer={layer} />
                            </Td>
                        </Tr>
                    </Tbody>
                );
            })}
        </Table>
    );
}

export default DeploymentComponentVulnerabilitiesTable;
